/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.document;

import cn.ac.nercis.pes.common.constant.common.ReportConstants;
import cn.ac.nercis.pes.common.utils.DataUtils;
import cn.ac.nercis.pes.common.constant.evaluation.ReportType;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.model.dto.report.EvaluationReportDTO;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.dal.report.EvaluationReportRepository;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.report.EvaluationReportDO;
import cn.ac.nercis.pes.repository.utils.DbUtils;
import cn.ac.nercis.pes.service.document.report.ReportOverallEvaluationDataService;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;

/**
 * 测评报告信息服务
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class ReportInfoService {
    private static final String SECURITY_LEVEL_FORMAT = "第%s级";

    private final EvaluationReportRepository evaluationReportRepository;
    private final SystemRepository systemRepository;
    private final ReportOverallEvaluationDataService overallEvaluationDataService;

    public ReportInfoService(EvaluationReportRepository evaluationReportRepository,
                             SystemRepository systemRepository,
                             ReportOverallEvaluationDataService overallEvaluationDataService) {
        this.evaluationReportRepository = evaluationReportRepository;
        this.systemRepository = systemRepository;
        this.overallEvaluationDataService = overallEvaluationDataService;
    }

    /**
     * 修复重复数据
     */
    public void repairDuplicateData(@NonNull String systemId){
        var source = this.evaluationReportRepository.findBySystemIdAndReportType(systemId, ReportType.REPORT);
        var duplicateData = DbUtils.findDuplicateData(source);
        if(CollectionUtils.isNotEmpty(duplicateData)){
            this.evaluationReportRepository.deleteAll(duplicateData);
        }
    }

    public Result<EvaluationReportDTO> getEvaluationReportInfo(@NonNull String systemId){
        this.repairDuplicateData(systemId);
        SystemDO systemDO = systemRepository.findById(systemId).orElse(null);
        if(Objects.isNull(systemDO)){
            return Result.failed(ResultCode.SYSTEM_NOT_EXIST);
        }
        String score = overallEvaluationDataService.getOverallScore(systemDO);
        EvaluationReportDTO reportInfo = DataUtils.findFirst(evaluationReportRepository.findBySystemIdAndReportType(systemId,ReportType.REPORT))
                .map(er->EvaluationReportDTO.builder()
                        .id(er.getId())
                        .systemId(er.getSystemId())
                        .code(er.getCode())
                        .distributionRange(er.getDistributionRange())
                        .securityLevel(String.format(SECURITY_LEVEL_FORMAT,systemDO.getSecurityLevel().getName()))
                        .conclusion(overallEvaluationDataService.getEvalResult(systemDO))
                        .score(score)
                        .build())
                .orElse(EvaluationReportDTO.builder()
                        .systemId(systemId)
                        .code(defaultReportCode(systemDO))
                        .distributionRange(defaultDistributionRange())
                        .securityLevel(String.format(SECURITY_LEVEL_FORMAT,systemDO.getSecurityLevel().getName()))
                        .conclusion(overallEvaluationDataService.getEvalResult(systemDO))
                        .score(score)
                        .build());
        return Result.success(reportInfo);
    }

    public Result<EvaluationReportDTO> saveEvaluationReportInfo(@NonNull EvaluationReportDTO reportDTO){
        try{
            if(StringUtils.isEmpty(reportDTO.getId())){
                return create(reportDTO);
            }else{
                return modify(reportDTO);
            }
        }catch (Exception e){
            log.error("保存密评报告信息出错：",e);
            return Result.failed();
        }
    }

    public synchronized void setGenerateReportTime(@NonNull String systemId, LocalDateTime reportTime){
        EvaluationReportDO entity = DataUtils.getFirst(evaluationReportRepository.findBySystemIdAndReportType(systemId,ReportType.REPORT));
        if(Objects.isNull(entity)){
            return;
        }
        entity.setReportTime(reportTime);
        entity.setModifyTime(LocalDateTime.now());
        evaluationReportRepository.save(entity);
    }

    private Result<EvaluationReportDTO> create(EvaluationReportDTO reportDTO){
        if(evaluationReportRepository.existsBySystemIdAndReportType(reportDTO.getSystemId(),ReportType.REPORT)){
            return Result.failed(ResultCode.EVALUATION_REPORT_INFO_EXIST);
        }
        EvaluationReportDO reportDO = EvaluationReportDO.builder()
                .systemId(reportDTO.getSystemId())
                .reportType(ReportType.REPORT)
                .code(reportDTO.getCode())
                .distributionRange(reportDTO.getDistributionRange())
                .build();
        reportDO.setCreateTime(LocalDateTime.now());
        return Optional.of(reportDO)
                .map(evaluationReportRepository::save)
                .map(entity->{
                    reportDTO.setId(entity.getId());
                    return reportDTO;
                })
                .map(Result::success)
                .orElse(Result.failed());
    }

    private Result<EvaluationReportDTO> modify(EvaluationReportDTO reportDTO){
        EvaluationReportDO entity = evaluationReportRepository.findById(reportDTO.getId()).orElse(null);
        if(Objects.isNull(entity)){
            return Result.failed(ResultCode.EVALUATION_REPORT_INFO_NOT_EXIST);
        }
        entity.setCode(reportDTO.getCode());
        entity.setDistributionRange(reportDTO.getDistributionRange());
        entity.setModifyTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(evaluationReportRepository::save)
                .map(er-> reportDTO)
                .map(Result::success)
                .orElse(Result.failed());
    }

    private String defaultReportCode(SystemDO systemDO){
        //项目编号+系统顺序号
        String reportCode = String.format("%s-%02d",systemDO.getProject().getNumber(),systemDO.getSort());
        return ReportConstants.PES_REPORT_CODE_PREFIX.concat(reportCode);
    }

    private String defaultDistributionRange(){
        return "本报告一式三份，其中一份提交密码管理部门，一份提交被测单位，一份由密评机构留存。";
    }
}